/*
 * XTable.java
 * 
 * Copyright (c) 2006 - 2007
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.xfc.table;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.MatteBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;

import org.xfc.XApp;
import org.xfc.components.XIconButton;
import org.xfc.util.XListenerManager;

import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.TextFilterator;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.swing.GlazedListsSwing;
import ca.odell.glazedlists.swing.TextComponentMatcherEditor;

import com.jgoodies.forms.layout.FormLayout;

// TODO: add the bean table column implementation

// TODO: most sorts shouldn't take into account case
//       use the locale's collator thingy?

// TODO: the sort is not stable -

// TODO: on startup, columns should be sized at least wide enough to display their header content

// TODO: selected rows need to be the same after a sort or a filter

// TODO: itemCountLabel should be parameterized

// TODO: have a default sort column option -


/**
 * 
 * 
 * 
 * 
 * @author Devon Carew
 */
public class XTable
	extends JPanel
{
	// TODO: don't hard code - use the uiconstants class
	static final Color BORDER_COLOR = Color.gray; //UIManager.getColor("controlShadow");
	
	private JTable 			table;
	private JScrollPane		scrollPane;
	
	private EventList		rowObjects;
	private SortedList		sortedList;
	
	private XTableModel		tableModel;
	
	private JPanel						filterPanel;
	private XDefaultFilterComponent 	filterComponent;
	private TextComponentMatcherEditor	filterMatcher;
	private JLabel						itemCountLabel;
	
	private XTableColumn	currentSortColumn;
	private boolean			sortAscending = true;
	
	private XRowHeader		rowHeader;
	
	private List			tableActions = new ArrayList();
	private JPanel			actionPanel;
	
	private XListenerManager	actionListenerManager = new XListenerManager();
	private XListenerManager	filterListenerManager = new XListenerManager();
	
	private int				oldTableRowCount = -1;
	
	private boolean			tableEditable;
	
	// TODO: the icon for sort_up needs to be redone - the shadows are the wrong way.
	private static Icon		SORT_ASCENDING_ICON = XApp.getApp().getResources().getIcon("/eclipse/misc/sort_up.gif");
	private static Icon		SORT_DESCENDING_ICON = XApp.getApp().getResources().getIcon("/eclipse/misc/sort_down.gif");
	private static Icon		SORT_NONE_ICON = null; //XApp.getApp().getResources().getIcon("/eclipse/misc/sort_none.gif");
	
	
	/**
	 * 
	 */
	public XTable()
	{
		tableModel = new XTableModel();
		
		table = new JTable(tableModel) {
			public Object getValueAt(int row, int column) {
				return XTable.this.getValueAt(row, column);
			}
			public boolean isCellEditable(int row, int column) {
				return XTable.this.isCellEditable(row, column);
			}
			public void setValueAt(Object newValue, int row, int column) {
				XTable.this.setValueAt(newValue, row, column);
			}
		};
		table.setAutoCreateColumnsFromModel(false);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		table.getTableHeader().setBorder(new MatteBorder(0, 0, 1, 0, BORDER_COLOR));
		
		scrollPane = new JScrollPane(table);
		
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		scrollPane.getViewport().setBackground(table.getBackground());
		JLabel upperRightCorner = createCornerScrollComponent();
		upperRightCorner.setBorder(new MatteBorder(0, 1, 1, 0, BORDER_COLOR));
		scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, upperRightCorner);
		
		rowHeader = new XRowHeader(this);
		
		scrollPane.setRowHeaderView(rowHeader);
		
		// If we have a row header, then we need this filler component
		JLabel upperLeftCorner = createCornerScrollComponent();
		upperLeftCorner.setBorder(new MatteBorder(0, 0, 1, 1, BORDER_COLOR));
		scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, upperLeftCorner);
		
		JLabel lowerLeftCorner = createCornerScrollComponent();
		lowerLeftCorner.setBorder(new MatteBorder(1, 0, 0, 1, BORDER_COLOR));
		scrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER, lowerLeftCorner);
		
		// Sorting.
		table.getTableHeader().addMouseListener(new ColumnHeaderClickHandler());
		table.getTableHeader().setDefaultRenderer(new XTableHeaderRenderer());
		
		// Filtering.
		filterPanel = new JPanel(new FormLayout(
			"pref 6dlu pref",
			"pref"
		));
		
		filterComponent = new XDefaultFilterComponent();
		
		filterComponent.getTextField().addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { fireFilterAction(); }
		});
		filterComponent.getTextField().getDocument().addDocumentListener(new DocumentListener() {
			public void changedUpdate(DocumentEvent e) { fireFilterChangedEvent(); }
			public void insertUpdate(DocumentEvent e) { fireFilterChangedEvent(); }
			public void removeUpdate(DocumentEvent e) { fireFilterChangedEvent(); }
		});
		
		itemCountLabel = new JLabel();
		
		filterPanel.add(itemCountLabel,		"1, 1, r, c");
		filterPanel.add(filterComponent,	"3, 1, r, c");
		
		// The actions panel.
		actionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
		actionPanel.setVisible(false);
		actionPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
		
		filterMatcher = new TextComponentMatcherEditor(
			filterComponent.getTextField(), new XTableTextFilterator());
		
		setLayout(new FormLayout(
			"min:grow", 
			"pref 2dlu min:grow 0dlu pref"
		));
		
		add(filterPanel,	"1, 1, r, c");
		add(scrollPane, 	"1, 3, f, f");
		add(actionPanel,	"1, 5, l, c");
		
		tableModel.addTableModelListener(new TableModelListener() {
			public void tableChanged(TableModelEvent event) {
				if (oldTableRowCount != getTable().getRowCount())
					rowCountChanged();
			}
		});
		
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent event) {
				// Make sure that the row headers repaint when the selection changes.
				if (rowHeader != null)
					rowHeader.repaint();
				
				// Notify the XTableSelectionActions.
				for (Iterator itor = tableActions.iterator(); itor.hasNext(); )
				{
					XTableSelectionAction action = (XTableSelectionAction)itor.next();
					
					action.selectionChanged(XTable.this);
				}
			}
		});
		
		table.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent event) {
				if (event.getClickCount() >= 2) {
					fireDoubleClickEvent();
				}
			}
		});
		
		updateItemCountLabel();
		
		setRowObjects(new BasicEventList());
	}
	
	private void rowCountChanged()
	{
		oldTableRowCount = getTable().getRowCount();
		
		updateItemCountLabel();
		
		if (rowHeader.isVisible())
			rowHeader.repaint();
	}
	
	public void addDoubleClickListener(ActionListener listener)
	{
		actionListenerManager.addListener(listener);
	}

	public void removeDoubleClickListener(ActionListener listener)
	{
		actionListenerManager.removeListener(listener);
	}
	
	private void fireDoubleClickEvent()
	{
		actionListenerManager.notifyListeners("actionPerformed", new ActionEvent(this, 0, null));
	}
	
	public void addFilterListener(XFilterListener listener)
	{
		filterListenerManager.addListener(listener);
	}

	public void removeFilterListener(XFilterListener listener)
	{
		filterListenerManager.removeListener(listener);
	}
	
	private void fireFilterChangedEvent()
	{
		filterListenerManager.notifyListeners("filterChanged", this, getFilterValue());
	}
	
	private void fireFilterAction()
	{
		filterListenerManager.notifyListeners("filterAction", this, getFilterValue());
	}
	
	public void addSelectionAction(XTableSelectionAction action)
	{
		tableActions.add(action);
		
		addTableComponent(new JButton(action));
		
		action.selectionChanged(this);
	}
	
	public void addTableComponent(JComponent component)
	{
		if (actionPanel.getComponentCount() != 0)
			actionPanel.add(Box.createHorizontalStrut(8));
		
		actionPanel.add(component);
		
		if (!actionPanel.isVisible())
			actionPanel.setVisible(true);
		else
			actionPanel.validate();
	}
	
	public void addColumn(XTableColumn column)
	{
		table.addColumn(column);
	}
	
	public void removeAllColumns()
	{
		while (table.getColumnCount() > 0)
			table.removeColumn(table.getColumnModel().getColumn(0));
	}
	
	public void setRowObjects(EventList rowObjects)
	{
		if (this.rowObjects != null)
			sortedList.dispose();
		
		this.rowObjects = rowObjects;
		
		if (rowObjects == null)
			rowObjects = new BasicEventList();
		
		FilterList filterList = new FilterList(rowObjects, filterMatcher);
		
		sortedList = new SortedList(filterList, getCurrentComparator());
		// For editable tables, don't sort immediately after an edit.
		sortedList.setMode(SortedList.AVOID_MOVING_ELEMENTS);
		
		tableModel.setEventList(sortedList);
		
		rowCountChanged();
	}
	
	public void setHasFilter(boolean hasFilter)
	{
		filterPanel.setVisible(hasFilter);
		
		if (!hasFilter)
			clearFilter();
	}	
	
	public void setHasRowHeader(boolean hasRowHeader)
	{
		if (rowHeader != null)
		{
			rowHeader.setVisible(hasRowHeader);
			
			if (!rowHeader.isVisible())
			{
				scrollPane.setRowHeaderView(null);
				
				scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, null);
				scrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER, null);
			}
		}
	}	
	
	public XRowHeader getRowHeader()
	{
		return rowHeader;
	}

	public JTable getTable()
	{
		return table;
	}

	public EventList getAllRowObjects()
	{
		return rowObjects;
	}
	
	public EventList getVisibleRowObjects()
	{
		return sortedList;
	}
	
	public Object getRowObject(int row)
	{
		return getVisibleRowObjects().get(row);
	}
	
	public void clearFilter()
	{
		filterComponent.clearFilter();
	}
	
	public String getFilterValue()
	{
		return filterComponent.getFilterValue();
	}
	
	public boolean isTableEditable()
	{
		return tableEditable;
	}
	
	public void setTableEditable(boolean tableEditable)
	{
		this.tableEditable = tableEditable;
	}
	
	private XTableColumn getXTableColumn(int columnIndex)
	{
		if (columnIndex < 0)
			return null;
		
		return (XTableColumn)table.getColumnModel().getColumn(columnIndex);
	}
	
	private int getXTableColumnCount()
	{
		return table.getColumnModel().getColumnCount();
	}
	
	private Object getValueAt(int row, int column)
	{
		// The row index here is the visible row index -
		
		Object 			rowObject = sortedList.get(row);
		XTableColumn 	tableColumn = getXTableColumn(column);
		
		return tableColumn.getValue(rowObject);
	}
	
	private boolean isCellEditable(int row, int column)
	{
		if (!isTableEditable())
			return false;
		
		// TODO: check the row object?
		//Object 		rowObject = sortedList.get(row);
		XTableColumn 	tableColumn = getXTableColumn(column);
		
		return tableColumn.isEditable();
	}
	
	private void setValueAt(Object newValue, int row, int column)
	{
		Object 			rowObject = sortedList.get(row);
		XTableColumn 	tableColumn = getXTableColumn(column);
		
		tableColumn.setValue(rowObject, newValue);
	}
	
	private void toggleSort(XTableColumn column)
	{
		if (currentSortColumn == column)
		{
			if (sortAscending)
			{
				sortAscending = false;
			}
			else
			{
				currentSortColumn = null;
				sortAscending = true;
			}
		}
		else
		{
			currentSortColumn = column;
			sortAscending = true;
		}
		
		sortedList.setComparator(getCurrentComparator());
	}
	
	private void autoSizeColumn(int columnIndex)
	{
		// TODO: might be a view column index - need to check.
		
		XTableColumn column = getXTableColumn(columnIndex);
		
		if (column == null)
			return;
		
		Component c = getTable().getTableHeader().getDefaultRenderer().getTableCellRendererComponent(
			getTable(), column.getHeaderValue(), false, false, 0, columnIndex);
		
		int prefWidth = c.getMinimumSize().width;
		
		// Account for the sorting icon.
		if (currentSortColumn != column && column.isSortable())
			prefWidth += SORT_ASCENDING_ICON.getIconWidth();
		
		EventList list = getVisibleRowObjects();
		
		TableCellRenderer renderer = getTable().getCellRenderer(0, columnIndex);
		
		try
		{
			list.getReadWriteLock().readLock().lock();
			
			for (int i = 0; i < list.size(); i++)
			{
				Component c2 = getTable().prepareRenderer(renderer, i, columnIndex);
				
				prefWidth = Math.max(prefWidth, c2.getMinimumSize().width + 1);
			}
		}
		finally
		{
			list.getReadWriteLock().readLock().unlock();
		}
		
		if (prefWidth != column.getWidth())
			column.setPreferredWidth(prefWidth);
	}
	
	NumberFormat nf = NumberFormat.getIntegerInstance();
	
	private void updateItemCountLabel()
	{
		String text;
		
		int numItems = getAllRowObjects() == null ? 0 : getAllRowObjects().size();
		int visItems = getVisibleRowObjects() == null ? 0 : getVisibleRowObjects().size();
		
		if (visItems == numItems)
			text = nf.format(numItems);
		else
			text = nf.format(visItems) + " of " + nf.format(numItems);
		
		text = text + " items";
		
		if (!text.equals(itemCountLabel.getText()))
			itemCountLabel.setText(text);
	}
	
	private Comparator getCurrentComparator()
	{
		if (currentSortColumn == null)
			return null;
		
		return new Comparator() {
			public int compare(Object rowObject1, Object rowObject2) {
				Object sortObject1 = currentSortColumn.getSortValue(rowObject1);
				Object sortObject2 = currentSortColumn.getSortValue(rowObject2);
				
				int compareValue = currentSortColumn.getColumnComparator().compare(sortObject1, sortObject2);
				
				if (compareValue == 0)
				{
					// TODO:
					// return a comparison based on the current relative position of the rows
					// simulating a stable sort
					
					return 0;
				}
				else
				{
					return sortAscending ? compareValue : -1 * compareValue;
				}
			}
		};
	}
	
    private JLabel createCornerScrollComponent()
    {
        JLabel label = new JLabel();
        
        label.setOpaque(true);
        
        return label;
    }
    
	/**
	 * Renders a sort icon for column headers.
	 */
	class XTableHeaderRenderer
		extends JPanel
		implements TableCellRenderer
	{
		private JLabel textLabel1 = new JLabel();
		private JLabel textLabel2 = new JLabel();
		private JLabel iconLabel = new JLabel();
		
		private Dimension largestSize = new Dimension();
		
		public XTableHeaderRenderer()
		{
			super(new BorderLayout());
			
			JPanel temp = new JPanel(new BorderLayout());
			
			temp.add(textLabel1, BorderLayout.NORTH);
			temp.add(textLabel2, BorderLayout.SOUTH);
			
			add(Box.createHorizontalStrut(2), BorderLayout.WEST);
			add(temp, BorderLayout.CENTER);
			add(iconLabel, BorderLayout.EAST);
			
			textLabel1.setHorizontalAlignment(JLabel.CENTER);
			textLabel2.setHorizontalAlignment(JLabel.CENTER);
			
			setBorder(BorderFactory.createRaisedBevelBorder());
		}
		
		public Component getTableCellRendererComponent(JTable table,
			Object value, boolean isSelected, boolean hasFocus, int rowIndex, int columnIndex)
		{
			XTableColumn column = getXTableColumn(columnIndex);
			
			Object headerValue = column.getHeaderValue();
			
			if (headerValue instanceof String)
			{
				String title = (String)headerValue;
				
				textLabel1.setIcon(null);
				
				if (title.indexOf('\n') != -1)
				{
					int index = title.indexOf('\n');
					textLabel1.setText(title.substring(0, index));
					textLabel2.setText(title.substring(index + 1));
					textLabel2.setVisible(true);
					
				}
				else
				{
					textLabel1.setText(title);
					textLabel2.setVisible(false);
				}
			}
			else if (headerValue instanceof Icon)
			{
				textLabel1.setText("");
				textLabel1.setIcon((Icon)headerValue);
				textLabel2.setVisible(false);
			}
			else
			{
				throw new IllegalArgumentException("Bad value for header value: " + headerValue);
			}
			
			if (column.isSortable())
			{
				if (currentSortColumn != column)
					iconLabel.setIcon(SORT_NONE_ICON);
				else if (sortAscending)
					iconLabel.setIcon(SORT_ASCENDING_ICON);
				else
					iconLabel.setIcon(SORT_DESCENDING_ICON);
			}
			else
			{
				iconLabel.setIcon(null);
			}
			
			// Note that this hackery is because we share column renderers across all columns in a table.
			Dimension actualPrefSize = super.getPreferredSize();
			
			largestSize.width = Math.max(largestSize.width, actualPrefSize.width);
			largestSize.height = Math.max(largestSize.height, actualPrefSize.height);
			
			return this;
		}

		public Dimension getPreferredSize()
		{
			return largestSize;
		}
	}
	
	class XTableTextFilterator
		implements TextFilterator
	{
		public void getFilterStrings(List list, Object rowObject)
		{
			// TODO: is this only filtering on visible columns? Check -
			
			for (int i = 0; i < getXTableColumnCount(); i++)
			{
				XTableColumn column = getXTableColumn(i);
				
				if (!column.isFilterable())
					continue;
				
				String filterValue = column.getFilterText(rowObject);
				
				if (filterValue != null)
					list.add(filterValue);
			}
		}
	}
	
	private class ColumnHeaderClickHandler
		extends MouseAdapter
	{
        private boolean mouseEventIsPerformingPopupTrigger = false;

        public ColumnHeaderClickHandler()
        {
        	
        }

        public void mouseClicked(MouseEvent event)
        {
            if (mouseEventIsPerformingPopupTrigger || event.isPopupTrigger())
            	return;
            
            // TODO: this is a bit hard coded.
            if (event.getButton() != MouseEvent.BUTTON1)
            	return;
            
            TableColumnModel columnModel = table.getColumnModel();
            int viewColumn = columnModel.getColumnIndexAtX(event.getX());
            
            Rectangle rect = getTable().getTableHeader().getHeaderRect(viewColumn);
            
            if (event.getX() <= (rect.x + 3))
            {
            	if (event.getClickCount() >= 2)
            		autoSizeColumn(viewColumn - 1);
            }
            else if (event.getX() >= (rect.x + rect.width - 4))
            {
            	if (event.getClickCount() >= 2)
            		autoSizeColumn(viewColumn);
            }
            else
            {
	            //int column = table.convertColumnIndexToModel(viewColumn);
	            
	            XTableColumn column = getXTableColumn(viewColumn);
	            
	            if (column != null && column.isSortable())
	            	toggleSort(column);
            }
        }

        public void mousePressed(MouseEvent mouseEvent)
        {
            mouseEventIsPerformingPopupTrigger = mouseEvent.isPopupTrigger();
        }
    }

}

class XTableModel
	extends AbstractTableModel
{
	private TransformedList	transformedList;
	
	private ListEventListener listEventListener = new ListEventListener() {
		public void listChanged(ListEvent listEvent) {
			handleListChanged(listEvent);
		}
	};
	
	public XTableModel()
	{
		setEventList(new BasicEventList());
	}
	
	protected void setEventList(EventList eventList)
	{
		TransformedList oldList = transformedList;
		
		try
		{
			eventList.getReadWriteLock().readLock().lock();
			
			transformedList = GlazedListsSwing.swingThreadProxyList(eventList);
			transformedList.addListEventListener(listEventListener);
		}
		finally
		{
			eventList.getReadWriteLock().readLock().unlock();
		}
		
		if (oldList != null)
			oldList.dispose();
		
		fireTableDataChanged();
	}
	
	public int getColumnCount()
	{
		return 0;
	}

	public int getRowCount()
	{
        try
        {
    		transformedList.getReadWriteLock().readLock().lock();
    		
            return transformedList.size();
        }
        finally
        {
        	transformedList.getReadWriteLock().readLock().unlock();
        }
	}
	
	public Object getRowObjectAt(int rowIndex)
	{
        try
        {
    		transformedList.getReadWriteLock().readLock().lock();
    		
            return transformedList.get(rowIndex);
        }
        finally
        {
        	transformedList.getReadWriteLock().readLock().unlock();
        }
	}
	
	public Object getValueAt(int rowIndex, int columnIndex)
	{
		throw new UnsupportedOperationException();
	}
	
	private void handleListChanged(ListEvent listEvent)
	{
        try
        {
    		transformedList.getReadWriteLock().readLock().lock();
    		
            // for all changes, one block at a time
            while (listEvent.nextBlock())
            {
                // get the current change info
                int startIndex = listEvent.getBlockStartIndex();
                int endIndex = listEvent.getBlockEndIndex();
                int changeType = listEvent.getType();
                
                // create a table model event for this block
                if (changeType == ListEvent.UPDATE)
                {
                	fireTableChanged(new TableModelEvent(this, startIndex, endIndex, TableModelEvent.ALL_COLUMNS, changeType));
                }
                else
                {
                	// TODO: it would be nice to be a little more selective - we are losing our selection.
                	
                	fireTableChanged(new TableModelEvent(this));
                }
            }
        }
        finally
        {
        	transformedList.getReadWriteLock().readLock().unlock();
        }
	}	
}

class XDefaultFilterComponent
	extends JPanel
{
	private JTextField 	textField;
	private XIconButton	clearFilterButton;
	
	public XDefaultFilterComponent()
	{
		setLayout(new FormLayout(
			"min 2dlu pref:grow 2dlu min",
			"pref"
		));
		
		textField = new JTextField();
		textField.setColumns(8);
		
		setBorder(BorderFactory.createCompoundBorder(
			textField.getBorder(), BorderFactory.createEmptyBorder(2, 4, 2, 2)));
		
		textField.setBorder(null);
		
		setBackground(textField.getBackground());
		setOpaque(true);
		
		clearFilterButton = new XIconButton(XApp.getApp().getResources().getIcon("/eclipse/misc/progress_rem.gif"));
		clearFilterButton.setRolloverIcon(XApp.getApp().getResources().getIcon("/eclipse/misc/delete_obj.gif"));
		clearFilterButton.setDisabledIcon(XApp.getApp().getResources().getIcon("/eclipse/misc/sort_none.gif"));
		clearFilterButton.setPushedIcon(clearFilterButton.getIcon());
		clearFilterButton.setEnabled(false);
		clearFilterButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				clearFilter();
			}
		});
		
		JLabel searchIcon = new JLabel(XApp.getApp().getResources().getIcon("/eclipse/misc/search_results_view.gif"));
		
		add(searchIcon, 		"1, 1");
		add(textField, 			"3, 1, f, b");
		add(clearFilterButton,	"5, 1");
		
		textField.getDocument().addDocumentListener(new DocumentListener() {
			public void changedUpdate(DocumentEvent event) {
				clearFilterButton.setEnabled(isFiltering());
			}
			public void insertUpdate(DocumentEvent event) {
				clearFilterButton.setEnabled(isFiltering());
			}
			public void removeUpdate(DocumentEvent event) {
				clearFilterButton.setEnabled(isFiltering());
			}
		});
	}
	
	public JTextField getTextField()
	{
		return textField;
	}
	
	public String getFilterValue()
	{
		return textField.getText().trim();
	}
	
	public boolean isFiltering()
	{
		return getFilterValue().length() > 0;
	}
	
	public void clearFilter()
	{
		textField.setText("");
	}
}
